package cn.virens.web.controller.weixin;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.virens.common.RequestUtil;
import cn.virens.exception.APIException;
import cn.virens.web.common.BaseJSONController;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.WxJsapiSignature;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSession;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.result.WxMpOAuth2AccessToken;
import me.chanjar.weixin.mp.bean.result.WxMpUser;

@Controller
@RequestMapping("wx")
public class WxOauth2Contorller extends BaseJSONController implements InitializingBean {
	private static final String scope = WxConsts.OAuth2Scope.SNSAPI_USERINFO;
	private static final String redirectURI = "http://wx.virens.cn/wx/authorization/callback";

	@Autowired
	@Qualifier("wxMpSessionManager")
	private WxSessionManager mWxSessionManager;

	@Autowired
	@Qualifier("springCacheManager")
	private CacheManager mCacheManager;

	@Autowired
	@Qualifier("wxMpService")
	private WxMpService mWxMpService;

	private Cache mCache;
	private String mCacheName;

	@RequestMapping(value = "authorization", method = RequestMethod.GET)
	public void authorization(HttpServletRequest request, HttpServletResponse response) throws IOException {
		if (mCache == null) { throw new APIException("Null", "Cache is error by not init!"); }

		String state = RandomUtil.randomString(32);
		String redirect = mWxMpService.oauth2buildAuthorizationUrl(redirectURI, scope, state);

		// 缓存回调地址
		mCache.put(state, RequestUtil.getValueStr(request, "url"));

		// 重定向到微信认证地址
		response.sendRedirect(redirect);
	}

	@RequestMapping(value = "authorization/callback", method = RequestMethod.GET)
	public void authorizationCallback(HttpServletRequest request, HttpServletResponse response) throws IOException {
		if (mCache == null) { throw new APIException("Null", "Cache is error by not init!"); }

		String ip = RequestUtil.getRemoteAddr(request);
		String code = RequestUtil.getValueStr(request, "code");
		String state = RequestUtil.getValueStr(request, "state");

		WxMpUser wxMpUser = null;
		WxMpOAuth2AccessToken wxMpAccessToken = null;
		try {
			wxMpAccessToken = mWxMpService.oauth2getAccessToken(code);
			wxMpUser = mWxMpService.oauth2getUserInfo(wxMpAccessToken, "zh_CN");

			// 缓存用户信息
			WxSession session = mWxSessionManager.getSession(wxMpAccessToken.getOpenId());
			session.setAttribute("oauth2:refreshToken", wxMpAccessToken.getRefreshToken());
			session.setAttribute("oauth2:accessToken", wxMpAccessToken.getAccessToken());
			session.setAttribute("oauth2:expiresIn", wxMpAccessToken.getExpiresIn());
			session.setAttribute("oauth2:unionId", wxMpAccessToken.getUnionId());
			session.setAttribute("oauth2:scope", wxMpAccessToken.getScope());
			session.setAttribute("oauth2:info", wxMpUser);
			session.setAttribute("oauth2:ip", ip);

			// 获取缓存中的回调地址
			String redirect = mCache.get(state, String.class);

			// 构造回调请求
			if (StrUtil.endWith(redirect, "?")) {
				response.sendRedirect(redirect + "oid=" + wxMpAccessToken.getOpenId());
			} else if (StrUtil.isNotEmpty(redirect)) {
				response.sendRedirect(redirect + "?oid=" + wxMpAccessToken.getOpenId());
			} else if (StrUtil.contains(redirect, '?')) {
				response.sendRedirect(redirect + "&oid=" + wxMpAccessToken.getOpenId());
			} else {
				throw new APIException("Error", "Undefind url");
			}
		} catch (WxErrorException e) {
			throw new APIException(e);
		} finally {
			mCache.evict(state);
		}
	}

	@ResponseBody
	@RequestMapping(value = "authorization/signature.json", method = RequestMethod.GET)
	public Model authorizationJs(HttpServletRequest request, HttpServletResponse response, Model model) throws IOException {
		String ip = RequestUtil.getRemoteAddr(request);
		String oid = RequestUtil.getValueStr(request, "oid");
		String url = RequestUtil.getValueStr(request, "url");

		try {
			WxSession session = mWxSessionManager.getSession(oid, false);
			if (session == null) { return renderFail(model, "未登录"); }
			if (ip != null && ip.equals(session.getAttribute("oauth2:ip"))) {
				WxJsapiSignature signature = mWxMpService.createJsapiSignature(url);

				Map<String, Object> data = new HashMap<>();
				data.put("url", signature.getUrl());
				data.put("appId", signature.getAppId());
				data.put("nonceStr", signature.getNonceStr());
				data.put("signature", signature.getSignature());
				data.put("timestamp", signature.getTimestamp());

				// 获取用户信息
				data.put("info", session.getAttribute("oauth2:info"));

				return renderSuccess(data, model);
			} else {
				return renderFail(model, "IP已切换");
			}
		} catch (Exception e) {
			return renderFail(model, e.getMessage());
		}
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		this.mCacheName = "wxmp:oauth2";
		this.mCache = mCacheManager.getCache(mCacheName);
	}
}
